//-----------------------------------------------------------------------------
//
// @file	Application.cpp
// @brief	Class to initiate application
// @author	Vinod Melapudi
// @date	01May2011
//
//-----------------------------------------------------------------------------

#include "assert.h"

#include "Application.h"
#include "Scene.h"

#include "OpenGLRender.h"
Renderer *g_renderer = OpenGLRender::GetInstance();


std::wstring s2w(const std::string& s)
{
	int len;
	int slength = (int)s.length() + 1;
	len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
	wchar_t* buf = new wchar_t[len];
	MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
	std::wstring r(buf);
	delete[] buf;
	return r;
}

Application* Application::GetInstance()
{
	static Application instance;
	return &instance;
}

Application::~Application()
{
}

Application::Application()
{
}

Renderer* Application::GetRenderer()
{
	return g_renderer;
}

AppConfig Application::GetConfig()
{
	return m_config;
}

int Application::Process( AppConfig& config )
{
	m_config = config;

	if ( !Init() )
		return 0;


	// Main loop
	MSG msg;
	while( TRUE )
	{
		// Pool for messages
		while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
		{
			// Send msg to WndProc
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		// If the message is WM_QUIT, exit the while loop
		if( msg.message == WM_QUIT )
			break;

		// Process scene
		if ( m_config.scene )		m_config.scene->Update();

		// Render frame
		Render();
	}



	UnInit();

	// Exit    
	return msg.wParam;
}

 

//
// @brief	Process windows messages
//
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) 
	{
	case WM_PAINT:
		{
			// Re-render frame
			PAINTSTRUCT paintStruct;
			HDC hdc = BeginPaint(hWnd, &paintStruct);
			Application::GetInstance()->Render();
			EndPaint(hWnd, &paintStruct);
		}
		break;

	case WM_ERASEBKGND:
		return true;
				
	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		{
			if ( Application::GetInstance()->HandleMessage( msg, wParam, lParam ) == false )
				return DefWindowProc(hWnd, msg, wParam, lParam);
		}

	}
   return 0;
}

//
// @brief	Init scene and renderer
//
bool Application::Init()
{
#ifdef _UNICODE
	std::wstring appName = s2w(m_config.appName);
#else
	std::string appName = m_config.appName;
#endif

	// Register the window class
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, 
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      appName.c_str(), NULL };
    RegisterClassEx( &wc );


	// Get window size from client area size
	RECT rectangle = { 0, 0, m_config.width, m_config.height };
	if ( !AdjustWindowRect( &rectangle, WS_CAPTION|WS_SYSMENU, false ) )
	{
		MessageBox( NULL, TEXT("Failed to setup openGL renderer."), appName.c_str(), MB_OK );
		return false;
	}


	// If requested window is within desktop...continue, or else resize requested window
	{
		// Get desktop size
		HWND desktop_wnd = GetDesktopWindow();
		RECT desktop_rect;
		GetWindowRect( desktop_wnd, &desktop_rect );

		// Make new window 90% of desktop
		if ( rectangle.right-rectangle.left >= desktop_rect.right-desktop_rect.left )
		{
			rectangle.right = 0.9*desktop_rect.right;
			rectangle.left = desktop_rect.left;
		}

		if ( rectangle.bottom-rectangle.top >= desktop_rect.bottom-desktop_rect.top )
		{
			rectangle.bottom = 0.9*desktop_rect.bottom;
			rectangle.top = desktop_rect.top;
		}
	}



    // Create window and get handle
    m_wnd = CreateWindow( appName.c_str(), appName.c_str(), 
                         WS_CAPTION|WS_SYSMENU, 
						 0, 0, 
						 rectangle.right-rectangle.left, 
						 rectangle.bottom-rectangle.top,
                         NULL, NULL, m_config.hInstance, NULL );

	assert( wc.hInstance == m_config.hInstance );

	// Get render-window size
	GetClientRect( m_wnd, &rectangle );
	m_config.width = (rectangle.right-rectangle.left);
	m_config.height = (rectangle.bottom-rectangle.top);


	// Init renderer
	if( !g_renderer->Init( m_wnd, m_config.width, m_config.height ) )
	{
		MessageBox( NULL, TEXT("Failed to setup openGL renderer."), appName.c_str(), MB_OK );
		return false;
	}

	// Init scene
	if ( m_config.scene )
	{
		m_config.scene->SetScreenDimension(m_config.width, m_config.height);
		if ( !m_config.scene->Init() )
		{
			MessageBox( NULL, TEXT("Failed to setup scene."), appName.c_str(), MB_OK );
			return false;
		}
	}

	ShowWindow( m_wnd, m_config.cmdShow );		// Display
	return true;
}

//
// @brief	Render scene
//
void Application::Render()
{
	if ( !g_renderer->Initialized() )
		return;

	g_renderer->PreRender();
	
	if ( m_config.scene )		m_config.scene->Render( g_renderer );
	
	g_renderer->PostRender();
}

//
// @brief	Uninit renderer and scene
//
void Application::UnInit()
{
	if ( m_config.scene )		m_config.scene->UnInit();
	
	g_renderer->UnInit();

#ifdef _UNICODE
	std::wstring appName = s2w(m_config.appName);
#else
	std::string appName = m_config.appName;
#endif

	UnregisterClass( appName.c_str(), m_config.hInstance );
}


bool Application::HandleMessage(UINT msg, WPARAM wParam, LPARAM lParam)
{
	return m_config.scene ? m_config.scene->HandleMessage( msg, wParam, lParam ) : false;
}


